package com.wwh.wwhspringmvc.context;

import com.wwh.wwhspringmvc.annotation.Autowired;
import com.wwh.wwhspringmvc.annotation.Controller;
import com.wwh.wwhspringmvc.annotation.Service;
import com.wwh.wwhspringmvc.xml.XMLParser;
import org.dom4j.DocumentException;

import java.io.File;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Albert
 * @date 2024/9/5-19:56
 * @describe 自己的Spring容器
 */
public class WwhWebApplicationContext {
    //保存扫描包的类的全路径
    private List<String> classPathList = new ArrayList<>();

    //容器，存放反射生成的对象
    public ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    private String configLocation;//spring容器的配置文件

    public WwhWebApplicationContext() {

    }

    public WwhWebApplicationContext(String configLocation) {
        this.configLocation = configLocation;
    }

    public void init() {
        String basePackage = XMLParser.getBasePackage(configLocation.split(":")[1]);
        String[] basePackages = basePackage.split(",");
        for (String aPackage : basePackages) {
            scanPackage(aPackage);
        }
        //System.out.println("classPath = " + classPathList);
        //将扫描到的类注入IOC容器
        executeInstance();
        //自动装配属性
        executeAutowired();
        System.out.println("ioc = " + ioc);
    }

    /**
     * @param pack 需要扫描的包
     */
    public void scanPackage(String pack) {
        //得到包所在的工作路径（绝对路径）
        //下面这句话是通过类的加载器得到指定包对应的工作路径
        URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));//正则表达式 . 需要转义
        //System.out.println("url = " + url);

        //根据得到的路径进行扫描
        String path = url.getFile();
        //在IO中把目录视为一个文件
        File dir = new File(path);
        for (File f : dir.listFiles()) {
            if (f.isDirectory()) {
                scanPackage(pack + "." + f.getName());
            } else {
                //这个时候扫描的文件可能是.class文件，也可能是其他文件，而且.class文件还不一定是需要注入容器的文件
                //因此先保存到集合，后面注入时再进行处理
                String classPath = pack + "." + f.getName().replaceAll(".class", "");
                classPathList.add(classPath);
            }
        }
    }

    //将扫描到的类在满足条件的情况下反射放入ioc容器
    public void executeInstance() {
        //判断是否扫描到类
        if (classPathList.isEmpty()) {
            return;
        }

        //遍历进行反射
        try {
            for (String classFullPath : classPathList) {
                Class<?> aClass = Class.forName(classFullPath);
                if (aClass.isAnnotationPresent(Controller.class)) {
                    Object instance = aClass.newInstance();
                    //暂时默认为类名首字母小写
                    ioc.put(aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1), instance);
                } else if (aClass.isAnnotationPresent(Service.class)) {
                    Service service = aClass.getAnnotation(Service.class);
                    Object instance = aClass.newInstance();
                    String name = service.value();
                    if ("".equals(name)) {
                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            String beanName = anInterface.getSimpleName().substring(0, 1).toLowerCase() + anInterface.getSimpleName().substring(1);
                            ioc.put(beanName, instance);
                        }
                        ioc.put(aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1), instance);
                    } else {
                        ioc.put(name, instance);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //完成属性的自动装配
    public void executeAutowired() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            String key = entry.getKey();
            Object bean = entry.getValue();

            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = declaredField.getAnnotation(Autowired.class);
                    String beanName = autowired.value();
                    declaredField.setAccessible(true);
                    try {
                        if ("".equals(beanName)) {
                            Class<?> type = declaredField.getType();
                            beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
                        }
                        if (null == ioc.get(beanName)) {
                            throw new RuntimeException("容器中不存在 [" + beanName + "] 对象");
                        }
                        declaredField.set(bean, ioc.get(beanName));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}
